package com.engine.game;


import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Stack;
import aurelienribon.tweenengine.TweenManager;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.level.game.Configuration;




public abstract class GameEngine implements ApplicationListener {

	private InputHandler inputHandler;
	
	
	private TweenManager tweenManager;
	
	private ArrayList<Event> currentEventList;
	private ArrayList<Event> nextEventList;
	
	
	private Stack<GameState> stackOfGameStates;
	
    protected Configuration configuration;
    
    float accumulator = 0.0f;
    float t = 0.0f;
    static final float dt = 1/30f;
	
	
	private void dispatch(){
		
		for(Event e : currentEventList){
			
			
			
			
			
			if(e.getReceiver().equals("BROADCAST")){
				
				this.getTopState().receiveBroadcastEvent(e);
				this.configuration.receiveMessage(e);
				
			}
			else if(e.getReceiver().equals("CONFIGURATION")){
				
				
				
				this.configuration.receiveMessage(e);
				
			}
			else
			{
				this.getTopState().receiveEvent(e);
			}
			
			
		}
		
		
		currentEventList.clear();
	}
	
	
	
	
	private void swapEventLists(){
		
		ArrayList<Event> tmp = new ArrayList<Event>(currentEventList);
		currentEventList.clear();
		currentEventList.addAll(nextEventList);
		nextEventList.clear();
		nextEventList.addAll(tmp);
	
		
		
		
	}
	
	public void pushEvent(Event event){
		
		
		this.nextEventList.add(event);
		
		
	}
	
	
	
	public TweenManager getTweenManager(){
		return this.tweenManager;
	}
	
	
	public GameState getTopState(){
		return this.stackOfGameStates.peek();
	}
	
	public void removeTopState(){
		this.stackOfGameStates.pop().dispose();
	}
	
	public void changeTopState(GameState state){
		if(!this.stackOfGameStates.empty()){
			
			this.stackOfGameStates.pop().dispose();
		}
		
		this.stackOfGameStates.push(state);
		
		state.resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		
	}
	
	public void pushState(GameState state){
		this.stackOfGameStates.push(state);
	}
	
	public abstract void setConfiguration(GameEngine engine);
	
	
	@Override
	public void create() {
		
		
		
		
		this.setConfiguration(this);
		
		this.stackOfGameStates = new Stack<GameState>();
		
		this.currentEventList = new ArrayList<Event>();
		this.nextEventList = new ArrayList<Event>();
		
			
		try {
			this.configuration.setFirstState();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			
		
		
		
		this.inputHandler = new InputHandler();
		Gdx.input.setInputProcessor(inputHandler);
		Gdx.input.setCatchBackKey(true);
		this.inputHandler.setEngine(this);
		
		
		
		
		this.tweenManager = new TweenManager();
		
	}

	@Override
	public void resize(int width, int height) {
		this.getTopState().resize(width, height);
		
		
		
	}

	@Override
	public void render() {
		
		float frameTime = Gdx.graphics.getDeltaTime();
		
		if(frameTime > 0.25){
			frameTime = 0.25f;
			
		}
		
		accumulator += frameTime;
		
        while(accumulator >= dt){
			
			this.getTopState().getPhysicsWorld().update(dt);
			
			this.tweenManager.update(dt);
			
			
			
			t += dt;
			accumulator -= dt;
			
			
		}
        
        this.dispatch();
		
		
		try {
			this.getTopState().update();
		} catch (InstantiationException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IllegalAccessException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (ClassNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		
		
		try {
			this.configuration.update();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
        
        
        float alpha = (accumulator/dt);
        
        this.getTopState().render(alpha);
        
        
        this.swapEventLists();
	
		
	}

	@Override
	public void pause() {
		
		
	}

	@Override
	public void resume() {
		
		
	}

	@Override
	public void dispose() {
		this.inputHandler = null;
		
		this.tweenManager.killAll();
		this.tweenManager = null;
		
		this.currentEventList.clear();
		this.currentEventList = null;
		
		this.nextEventList.clear();
		this.nextEventList = null;
		
		for(GameState s : this.stackOfGameStates){
			s.dispose();
		}
		
		this.stackOfGameStates.clear();
		this.stackOfGameStates = null;
		
		this.configuration.dispose();
		this.configuration = null;
		
	}




	
	
}
